(* check all used variables *)

use "ast.sml";

infix ++;
infix \\;

local val rec op \\ : id list * id -> id list =
       fn ([], _) => []
	| (i::is, i') => if i'=i
			 then is
			 else i::(is \\ i')
in val op ++ : id * id list -> id list =
    fn (i, is) => i::(is \\ i)
end;

val rec op @ : id * id list -> bool =
 fn (i', []) => false
  | (i', i::is) => i'=i orelse (i' @ is);

local val rec checkVarsExp : (id list * exp) -> (bool * id list) =
       fn (is, IdExp i) => let val b = i @ is
			   in ( if not (i @ is)
				then print (i ^ ": used before initialized\n")
				else ignore 0
			      ; (i @ is, is))
			   end
	| (is, NumExp _) => (true, is)
	| (is, OpExp (l, _, r)) => let val (b, is) = checkVarsExp (is, l)
				   in let val (b', is') = checkVarsExp (is, r)
				      in (b andalso b', is')
				      end
				   end
	| (is, EseqExp (s, e)) => let val (b, is) = checkVarsStm (is, s)
				  in let val (b', is') = checkVarsExp (is, e)
				     in (b andalso b', is')
				     end
				  end
    and checkVarsExps : (id list * exp list) -> (bool * id list) =
     fn (is, []) => (true, is)
      | (is, (e :: es)) => let val (b, is) = checkVarsExp (is, e)
			   in let val (b', is') = checkVarsExps (is, es)
			      in (b andalso b', is')
			      end
			   end
    and checkVarsStm : (id list * stm) -> (bool * id list) =
     fn (is, CompoundStm (s, s')) => let val (b, is) = checkVarsStm (is, s)
				     in let val (b', is') = checkVarsStm (is, s')
					in (b andalso b', is')
					end
				     end
      | (is, AssignStm (i, e)) => let val (b, is) = checkVarsExp (is, e)
				  in (b, i++is)
				  end
      | (is, PrintStm es) => checkVarsExps (is, es)
in val checkVars : prg -> bool =
    fn p => let val (b, _) = checkVarsStm ([], p)
	    in b
	    end
end;

use "progs.sml";
use "assert.sml";

assert (     checkVars prog1,  "P1");
assert (not (checkVars prog2), "P2");
assert (     checkVars prog3,  "P3");
assert (     checkVars prog4,  "P4");
assert (not (checkVars prog5), "P5");
assert (     checkVars prog6,  "P6");
assert (     checkVars prog7,  "P7");
assert (     checkVars prog8,  "P8");
assert (     checkVars prog9,  "P9");
assert (     checkVars prog10, "P10");
assert (     checkVars prog11, "P11");
assert (     checkVars prog12, "P12");
assert (     checkVars prog13, "P13");
assert (     checkVars prog14, "P14");
assert (     checkVars prog15, "P15");
assert (     checkVars prog16, "P16");

OS.Process.exit OS.Process.success;

